Popular Searches
Popular Course Categories
Popular Courses

Optimizing Components Using React.memo

Optimizing Components Using React.memo

Performance Optimization and Memory Management

Optimizing Components Using React.memo in React Native

Course: React Native Training

Topic: Optimizing Components Using React.memo

Course Link: React Native Training at JustAcademy

Demo Link: Register for a Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction to React.memo

React Native applications often contain multiple components, including headers, cards, lists, buttons, forms, profile sections, and navigation layouts. When a parent component re-renders, its child components may also re-render. If some children repeatedly render with the same props, this can create unnecessary work.

React.memo is a React API that can help optimize functional components by allowing React to skip a component re-render when its props have not changed. It is especially useful when a component renders frequently, receives the same props across multiple renders, and performs enough rendering work for skipping it to matter.

For example, a product list may contain many product cards. If the parent screen updates a search input or another unrelated piece of state, product cards whose props remain unchanged may not need to render again. Wrapping a product-card component in React.memo can help reduce that unnecessary work.

However, React.memo is a performance optimization, not a guarantee that a component will never render again. Components can still render when their own state or consumed context changes, and React may render them for other reasons.

Learning Objectives

  • Understand React rendering and component re-rendering.
  • Learn the purpose and syntax of React.memo.
  • Identify components that may benefit from memoization.
  • Understand how React compares props.
  • Use React.memo with React Native functional components.
  • Combine memoized components with useCallback and useMemo.
  • Understand custom comparison functions and their risks.
  • Identify common memoization mistakes.
  • Measure rendering performance before and after optimization.

2. Understanding React Rendering

Rendering is the process through which React calls a component to determine what its user interface should look like. When state or props change, React may render the component again and reconcile the resulting UI with the previous version.

2.1 What Causes a Component to Re-render?

  • State changes: A component's state is updated through a state setter.
  • Props change: A parent provides new prop values to a child.
  • Parent renders: A parent re-renders, and its children may be rendered again.
  • Context changes: A component consuming a context may re-render when the relevant context value changes.
  • External subscriptions: Components connected to external stores or libraries may update when their subscribed data changes.

2.2 Example Without React.memo

import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';

function ProductInfo({ name }) {
  console.log('ProductInfo rendered');

  return <Text>Product: {name}</Text>;
}

export default function App() {
  const [count, setCount] = useState(0);

  return (
    <View>
      <Text>Count: {count}</Text>

      <Button
        title="Increase Count"
        onPress={() => setCount(count + 1)}
      />

      <ProductInfo name="Wireless Headphones" />
    </View>
  );
}

When the button is pressed, the parent component updates its state. React renders the parent again, and ProductInfo can also be rendered again even though its name prop is still the same.

For a very small component, this may not cause a noticeable performance problem. The example demonstrates the principle rather than suggesting that every child component needs optimization.

3. What Is React.memo?

React.memo is a higher-order component-style API that wraps a component and returns a memoized version. React can reuse the previous rendered result when the parent renders again and the component's props compare equal.

The default comparison checks each prop using Object.is. This is a shallow, per-prop comparison; React does not automatically compare every nested property inside objects and arrays.

3.1 Basic Syntax

import React, { memo } from 'react';

const MemoizedComponent = memo(YourComponent);

You can also wrap the component directly when defining it:

import React, { memo } from 'react';
import { Text } from 'react-native';

const ProductInfo = memo(function ProductInfo({ name }) {
  return <Text>Product: {name}</Text>;
});

export default ProductInfo;

3.2 How React.memo Works

  1. The component renders for the first time.
  2. React remembers the previous props for the memoized component.
  3. The parent renders again.
  4. React compares the new props with the previous props.
  5. If the props compare equal, React can skip rendering the component.
  6. If the props differ, React renders the component again.

React may still render a memoized component for other reasons. Therefore, the purpose of React.memo is to avoid unnecessary rendering work, not to freeze a component permanently.

4. Implementing React.memo in React Native

React Native uses React components to build mobile interfaces, so React.memo works with ordinary functional components that render native components such as View, Text, Image, and Pressable.

4.1 Example: Memoized Product Component

import React, { memo, useState } from 'react';
import {
  View,
  Text,
  Button,
  StyleSheet,
} from 'react-native';

const ProductCard = memo(function ProductCard({ name, price }) {
  console.log('ProductCard rendered:', name);

  return (
    <View style={styles.card}>
      <Text>{name}</Text>
      <Text>Price: ₹{price}</Text>
    </View>
  );
});

export default function ProductScreen() {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text>Counter: {count}</Text>

      <Button
        title="Increase Counter"
        onPress={() => setCount(value => value + 1)}
      />

      <ProductCard name="Keyboard" price={1500} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  card: {
    padding: 12,
    marginTop: 10,
    borderWidth: 1,
    borderRadius: 8,
  },
});

4.2 Explanation

  • ProductCard is wrapped in memo.
  • The parent component maintains a counter in state.
  • Updating the counter causes the parent to render again.
  • The product card receives the same string and number props.
  • React can skip rendering the product card because its props have not changed.

When the product name or price changes, the component needs to render again so the user sees the updated information.

5. React.memo and Props Comparison

Understanding prop comparison is essential to using memoization correctly. React compares the old and new values of each prop using Object.is by default.

Prop Type Example Typical Comparison Result
String "Phone" Equal when the string values are the same.
Number 1500 Equal when the numeric values are the same.
Boolean true Equal when the boolean value is the same.
Object { name: "Phone" } Equal only when the object reference compares equal.
Array [1, 2, 3] Equal only when the array reference compares equal.
Function () => {} Equal only when the function reference compares equal.

5.1 Primitive Props

Primitive values such as strings, numbers, and booleans are usually straightforward. Passing the same primitive value across renders allows the default comparison to succeed for that prop.

<ProductCard name="Laptop" price={45000} />

If the parent renders again with the same values, the memoized component can skip rendering, provided its other props also compare equal.

5.2 Object Props

Objects are compared by reference, not by recursively comparing their properties. Creating a new object during every parent render can prevent memoization from working as expected.

// This object is recreated on each parent render.
<UserCard user={{ name: 'Asha', age: 25 }} />

Even if the object contains identical data, its new reference will usually compare unequal to the previous object.

5.3 Array Props

Arrays also use reference comparison. If an array is rebuilt during each render, a memoized child receiving that array may render again.

// A new array is created whenever the parent renders.
<TagList tags={['React Native', 'JavaScript']} />

When practical, avoid creating unnecessary new arrays and objects, or use useMemo when a stable reference is genuinely useful.

6. Combining React.memo with useCallback

useCallback caches a function reference between renders while its dependencies remain unchanged. It can help when a function is passed as a prop to a memoized child.

6.1 The Problem with Inline Functions

function ParentScreen() {
  const [count, setCount] = useState(0);

  return (
    <View>
      <Button
        title="Increase"
        onPress={() => setCount(count + 1)}
      />

      <MemoizedButton onPress={() => console.log('Pressed')} />
    </View>
  );
}

The inline callback passed to MemoizedButton is a new function reference whenever the parent renders. This can cause the child to render again even if its other props are unchanged.

6.2 Improved Example Using useCallback

import React, { memo, useCallback, useState } from 'react';
import {
  View,
  Text,
  Pressable,
} from 'react-native';

const MemoizedButton = memo(function MemoizedButton({ onPress }) {
  console.log('MemoizedButton rendered');

  return (
    <Pressable onPress={onPress}>
      <Text>Show Message</Text>
    </Pressable>
  );
});

export default function ParentScreen() {
  const [count, setCount] = useState(0);

  const handleMessage = useCallback(() => {
    console.log('Button pressed');
  }, []);

  return (
    <View>
      <Text>Count: {count}</Text>

      <Pressable onPress={() => setCount(value => value + 1)}>
        <Text>Increase Count</Text>
      </Pressable>

      <MemoizedButton onPress={handleMessage} />
    </View>
  );
}

6.3 Explanation

  • useCallback preserves the callback reference while its dependencies remain unchanged.
  • The memoized button receives the same function reference across counter updates.
  • React can skip rendering the button when all its props remain equal.
  • The button can still render when its own props or other relevant dependencies change.

Do not use useCallback for every function automatically. Use it when stable function identity provides a meaningful benefit, such as supporting memoization or stabilizing a hook dependency.

7. Combining React.memo with useMemo

useMemo caches the result of a calculation between renders while its dependencies remain unchanged. It is useful when a component passes an object, array, or calculated value to a memoized child.

7.1 Example Without useMemo

function ProfileScreen() {
  const [count, setCount] = useState(0);

  const user = {
    name: 'Asha',
    role: 'Developer',
  };

  return (
    <View>
      <Text>Count: {count}</Text>
      <ProfileCard user={user} />
    </View>
  );
}

The user object is recreated on each render. If ProfileCard is memoized, the new object reference can still trigger another render.

7.2 Improved Example Using useMemo

import React, { memo, useMemo, useState } from 'react';
import { View, Text } from 'react-native';

const ProfileCard = memo(function ProfileCard({ user }) {
  console.log('ProfileCard rendered');

  return (
    <View>
      <Text>Name: {user.name}</Text>
      <Text>Role: {user.role}</Text>
    </View>
  );
});

export default function ProfileScreen() {
  const [count, setCount] = useState(0);

  const user = useMemo(() => ({
    name: 'Asha',
    role: 'Developer',
  }), []);

  return (
    <View>
      <Text>Counter: {count}</Text>
      <ProfileCard user={user} />
    </View>
  );
}

Here, the object reference remains stable while the dependencies remain unchanged, allowing the memoized child to skip rendering during unrelated parent updates.

7.3 Important Considerations

  • Include every reactive value used by a memoized calculation in its dependency list.
  • Do not use useMemo as a substitute for correct state management.
  • Consider passing only the primitive values the child actually needs.
  • Use memoization when the benefit outweighs the additional complexity.

8. Using React.memo with React Native Lists

Lists are common places to investigate rendering performance because a screen may contain many rows or cards. React Native's FlatList virtualizes list content, but row components can still benefit from efficient prop handling.

8.1 Example: Memoized List Item

import React, { memo, useCallback, useState } from 'react';
import {
  View,
  Text,
  FlatList,
  Pressable,
} from 'react-native';

const ProductItem = memo(function ProductItem({ item, onSelect }) {
  return (
    <Pressable onPress={() => onSelect(item.id)}>
      <View>
        <Text>{item.name}</Text>
        <Text>₹{item.price}</Text>
      </View>
    </Pressable>
  );
});

const PRODUCTS = [
  { id: '1', name: 'Keyboard', price: 1500 },
  { id: '2', name: 'Mouse', price: 700 },
  { id: '3', name: 'Monitor', price: 12000 },
];

export default function ProductListScreen() {
  const [selectedId, setSelectedId] = useState(null);

  const handleSelect = useCallback((id) => {
    setSelectedId(id);
  }, []);

  const renderItem = useCallback(({ item }) => (
    <ProductItem item={item} onSelect={handleSelect} />
  ), [handleSelect]);

  return (
    <View>
      <Text>Selected product: {selectedId ?? 'None'}</Text>

      <FlatList
        data={PRODUCTS}
        keyExtractor={item => item.id}
        renderItem={renderItem}
      />
    </View>
  );
}

8.2 Why This Helps

  • ProductItem is memoized.
  • handleSelect has a stable reference while its dependencies remain unchanged.
  • renderItem also remains stable while its dependencies remain unchanged.
  • List rows with unchanged props can avoid unnecessary component rendering.

For more complex lists, consider whether passing an entire item object is necessary. If the parent recreates every item object after each update, memoized rows may still render. Keep data references stable where practical and update only the records that actually change.

Also consider FlatList properties such as initialNumToRender, windowSize, and maxToRenderPerBatch according to the screen's requirements. These settings address list rendering and virtualization, which are related to but different from React.memo.

9. Using React.memo with Custom Comparison Functions

By default, React.memo compares props using Object.is. In specific cases, a custom comparison function can determine whether the previous and next props would produce the same output and behavior.

9.1 Syntax

const MemoizedComponent = React.memo(
  YourComponent,
  arePropsEqual
);

The comparison function receives the previous props and the next props. It should return true only when the component can safely reuse its previous rendered result and behavior. Returning false tells React that it should render the component again.

9.2 Example

import React from 'react';
import { Text, View } from 'react-native';

const ScoreDisplay = React.memo(
  function ScoreDisplay({ score }) {
    return (
      <View>
        <Text>Score: {score.value}</Text>
      </View>
    );
  },
  (previousProps, nextProps) => {
    return previousProps.score.value === nextProps.score.value;
  }
);

export default ScoreDisplay;

This comparison checks the displayed score value. It is safe only if that value fully represents the relevant behavior and output of the component. If the component also depends on other score properties, those must be considered too.

9.3 Risks of Custom Comparisons

  • Forgetting to compare a prop can cause the component to display stale information.
  • Failing to compare callback props can preserve functions that capture outdated state.
  • Deep comparisons can be slower than rendering the component.
  • Custom comparisons add complexity and should be measured carefully.

Prefer the default comparison unless profiling demonstrates that a custom comparator is necessary and beneficial.

10. When React.memo Does Not Prevent Re-rendering

Memoization has specific boundaries. Understanding these boundaries prevents developers from expecting React.memo to control every render.

10.1 The Component's Own State Changes

If a memoized component updates its own state, React can render it again even if the parent props remain unchanged.

10.2 A Consumed Context Changes

A component that reads a context with useContext may render again when that context value changes. Wrapping the component in React.memo does not block updates from context that it consumes.

10.3 Props Have New References

New objects, arrays, or functions passed from the parent can cause the default prop comparison to fail, even when their contents appear identical.

10.4 React Chooses to Render

React.memo is a performance optimization. It does not guarantee that React will never render the component again.

11. React.memo and Context Optimization

Context is useful for sharing values such as themes, language settings, and session information. However, when a context value changes, components that consume it may re-render even if they are wrapped in React.memo.

11.1 Example Pattern

If a large component consumes a frequently changing context but only needs a small part of that context, split the component into a context-reading wrapper and a memoized child.

import React, { memo, useContext } from 'react';
import { Text, View } from 'react-native';

const UserContext = React.createContext(null);

const UserSummary = memo(function UserSummary({ name }) {
  return (
    <View>
      <Text>User: {name}</Text>
    </View>
  );
});

function UserSummaryContainer() {
  const user = useContext(UserContext);

  return <UserSummary name={user.name} />;
}

The wrapper responds to context changes and passes the required value to the memoized child. If the name remains the same, the child can skip rendering even when another context field changes.

12. Common Mistakes When Using React.memo

12.1 Memoizing Every Component

Not every component needs memoization. Tiny components that render infrequently may gain little benefit, while additional memoization can make code more difficult to understand.

12.2 Passing New Objects on Every Render

Creating fresh object references can defeat the default comparison. Pass only the values the child needs or use useMemo where a stable reference is beneficial.

12.3 Passing Inline Callbacks Everywhere

New callback references can trigger memoized children to render again. Consider useCallback for callbacks whose stable identity has a measurable benefit.

12.4 Using Memoization to Hide Incorrect Logic

If a component behaves incorrectly when it renders again, investigate the underlying bug. Components should render predictably from their current props, state, and context.

12.5 Writing Expensive Comparison Functions

A custom comparison that recursively inspects a large data structure can consume more time than rendering the component. Measure the comparison cost before adopting it.

12.6 Ignoring State and Context

React.memo primarily optimizes rendering caused by unchanged props. It does not block updates caused by the component's own state or consumed context.

13. Measuring Performance Before and After Optimization

Performance optimization should be based on evidence. A component rendering repeatedly does not automatically mean that it is causing a noticeable problem.

13.1 Use React Developer Tools

React Developer Tools provides profiling features that can help identify components that render often and understand the cost of rendering updates. Use the profiler to inspect interactions that feel slow.

13.2 Use Simple Development Logging

const ProductCard = React.memo(function ProductCard({ name }) {
  console.log('Rendering product:', name);

  return <Text>{name}</Text>;
});

Logging can demonstrate when a component function runs, but it is not a precise performance measurement. Development mode and Strict Mode can introduce additional renders or checks. Avoid relying on console output alone to judge production performance.

13.3 Performance Checklist

  • Identify a specific slow interaction.
  • Profile the screen before making changes.
  • Find components rendering unnecessarily with unchanged props.
  • Apply memoization to selected components.
  • Repeat the same interaction and profile again.
  • Compare the actual cost and user-perceived responsiveness.
  • Test production builds on representative devices.

14. React.memo and React Compiler

Modern React tooling may include React Compiler, which can automatically optimize component rendering and memoize values in supported projects. When the compiler is enabled and correctly configured, the need for manually adding React.memo may be reduced.

Whether React Compiler is available depends on the project's React version, framework, and build configuration. Check your project's actual setup before assuming automatic memoization is enabled.

Manual React.memo remains useful for understanding rendering behavior and maintaining projects where compiler-based optimization is not configured. Avoid adding redundant optimization layers without a clear reason.

15. Practical Project: Optimizing a Product List

Project Objective: Create a React Native product listing screen and optimize product cards using React.memo, useCallback, and useMemo where appropriate.

15.1 Project Requirements

  • Display a list of products with names and prices.
  • Allow users to select a product.
  • Include a counter or search input that updates the parent screen.
  • Prevent unaffected product cards from rendering unnecessarily.
  • Keep callback and data references stable where appropriate.
  • Measure rendering behavior before and after optimization.

15.2 Development Steps

  1. Create the React Native project and build the product list screen.
  2. Implement a basic product-card component.
  3. Add temporary render logging to understand the baseline behavior.
  4. Wrap the product-card component in React.memo.
  5. Use useCallback for callbacks passed to memoized children when appropriate.
  6. Use useMemo only for values whose stable references provide a benefit.
  7. Update a single product and verify that the correct information changes.
  8. Profile list scrolling and parent-state updates.
  9. Compare the results and document any measurable improvement.

15.3 Expected Outcome

The application should display the same correct information while avoiding unnecessary rendering work in components whose props remain unchanged. The project should demonstrate that memoization is most useful when applied to measured performance problems rather than added indiscriminately.

16. Best Practices for React.memo

  • Profile first and optimize components that actually affect performance.
  • Use memoization for frequently rendered components with stable props.
  • Keep components pure and their rendering logic predictable.
  • Pass the minimum information a child component needs.
  • Use useCallback when stable callback identity helps memoized children.
  • Use useMemo when a calculated value or reference benefits from caching.
  • Avoid expensive deep comparisons unless they have been measured and justified.
  • Remember that local state and consumed context can still trigger renders.
  • Use React Native list virtualization alongside component-level optimization.
  • Validate improvements on production-like builds and real devices.

17. Interview Questions and Answers

Q1. What is React.memo?

React.memo is a React API that can skip rendering a functional component when its props compare equal to the previous props.

Q2. Why is React.memo useful in React Native?

It can reduce unnecessary rendering work in components such as product cards, list rows, profile summaries, and other components that frequently receive unchanged props.

Q3. Does React.memo guarantee that a component will never re-render?

No. It is a performance optimization, not a guarantee. A component may render again because its state, consumed context, or other relevant inputs change.

Q4. How does React compare props by default?

React compares each prop using Object.is. This is not a recursive comparison of nested object properties.

Q5. Why can an object prop defeat memoization?

If the parent creates a new object on every render, the reference changes and the default comparison treats that prop as different.

Q6. What is the difference between React.memo and useMemo?

React.memo memoizes a component based on its props. useMemo caches the result of a calculation between renders while its dependencies remain unchanged.

Q7. What is the difference between React.memo and useCallback?

React.memo can skip a component render when props compare equal. useCallback caches a function reference so it can remain stable across renders while dependencies remain unchanged.

Q8. Can a memoized component update its own state?

Yes. A state update inside the memoized component can cause it to render again.

Q9. Can React.memo prevent context updates?

No. A component consuming a context may render again when that context value changes.

Q10. When should a custom comparison function be used?

Only when the default comparison is insufficient and profiling shows that a carefully written custom comparison is worthwhile. It must account for all props that affect output and behavior.

Q11. Is React.memo always beneficial?

No. Memoization adds comparison and maintenance costs. It is most useful when it avoids enough rendering work to justify those costs.

Q12. How can you verify whether React.memo improves performance?

Use React Developer Tools profiling, compare the same interaction before and after the change, and validate the result in a production-like build.

18. Quick Revision Checklist

  • Understand when React components re-render.
  • Know the purpose and syntax of React.memo.
  • Understand the default Object.is prop comparison.
  • Recognize new object, array, and function references.
  • Use useCallback for useful stable callback references.
  • Use useMemo for useful cached calculations or values.
  • Remember that state and context can still trigger rendering.
  • Use custom comparison functions carefully.
  • Profile components before and after optimization.
  • Optimize list rendering alongside component rendering where needed.
  • Avoid memoizing every component without a clear benefit.

19. Conclusion

React.memo is a valuable optimization tool for React Native applications when components repeatedly render with unchanged props. It can help improve the efficiency of list items, product cards, profile components, and other reusable UI elements.

To use it effectively, developers should understand prop identity, avoid unnecessary object and function references, and combine React.memo with useCallback or useMemo only when doing so provides a real benefit. Always measure performance, keep rendering logic correct, and prioritize improvements that make the application noticeably more responsive.

20. Learning Resources

whatsapp